 /*
 *	ME2 C++ SDK
 *	Copyright (c) 2010 _silencer
 *
 *	Permission is hereby granted, free of charge, to any person
 *	obtaining a copy of this software and associated documentation
 *	files (the "Software"), to deal in the Software without
 *	restriction, including without limitation the rights to use,
 *	copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the
 *	Software is furnished to do so, subject to the following
 *	conditions:
 *
 *	The above copyright notice and this permission notice shall be
 *	included in all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *	OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef UNREAL_SDK_H
#define UNREAL_SDK_H

#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include <string>
#include <hash_set>
#include <hash_map>

#pragma warning( disable : 4099 )
#pragma warning( disable : 4996 )

#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>

#include "unreal_offsets.h"
#include "unreal_core.h"
#include "unreal_templates.h"
#include "unreal_string.h"
#include "unreal_base.h"
#include "unreal_name.h"
#include "unreal_object.h"
#include "unreal_field.h"
#include "unreal_enum.h"
#include "unreal_const.h"
#include "unreal_property.h"
#include "unreal_struct.h"
#include "unreal_function.h"
#include "unreal_state.h"
#include "unreal_class.h"
#include "unreal_singleton.h"

namespace unreal
{
	class uevent_listener
	{
	public:

		uevent_listener()
			: fire_after_( false ), filter_on_caller_( 0 ), filter_on_function_( 0 )
		{
		}

		uevent_listener( bool fire_after, uobject* filter_on_caller = 0, ufunction* filter_on_function = 0 )
			: fire_after_( fire_after ), filter_on_caller_( filter_on_caller ), filter_on_function_( filter_on_function )
		{
		}

		virtual ~uevent_listener(){}
		virtual void on_unfiltered_event( uobject* caller, ufunction* function, void* parms, void* result ){};
		virtual void on_filtered_caller_event( ufunction* function, void* parms, void* result ){};
		virtual void on_filtered_function_event( uobject* caller, void* parms, void* result ){};
		virtual void on_filtered_event( void* parms, void* result ){};

		bool operator==( const uevent_listener& listener ) const
		{
			bool same_ptr( this == &listener );
			bool same_caller( filter_on_caller_ == listener.filter_on_caller_ );
			bool same_function( filter_on_function_ == listener.filter_on_function_ );

			return ( same_ptr && same_caller && same_function );
		}

		bool operator!=( const uevent_listener& listener ) const
		{
			bool same_ptr( this == &listener );
			bool same_caller( filter_on_caller_ == listener.filter_on_caller_ );
			bool same_function( filter_on_function_ == listener.filter_on_function_ );

			return !( same_ptr && same_caller && same_function );
		}

		uobject* get_filter_on_caller() const
		{
			return filter_on_caller_;
		}

		ufunction* get_filter_on_function() const
		{
			return filter_on_function_;
		}

		void set_filter_on_caller( uobject* filter_on_caller )
		{
			filter_on_caller_ = filter_on_caller;
		}

		void set_filter_on_function( ufunction* filter_on_function )
		{
			filter_on_function_ = filter_on_function;
		}

		bool is_valid() const
		{
			return ( this != 0 );
		}

		bool is_firing_before() const
		{
			return !fire_after_;
		}

		bool is_firing_after() const
		{
			return fire_after_;
		}

	protected:

		bool fire_after_;
		uobject* filter_on_caller_;
		ufunction* filter_on_function_;
	};

	#pragma pack(push)
	#pragma pack(1)

	struct uprocess_event_trampoline
	{
		uint8 origonal_code[5];
		uint8 jmp_cont_opcode;
		uint32 jmp_cont_address;
	};

	struct uprocess_event_jmp
	{
		uint8 jmp_hook_opcode;
		uint32 jmp_hook_address;
	};

	#pragma pack(pop)

	typedef std::vector<uevent_listener*> uevent_listener_list;
	typedef std::vector<uevent_listener*>::iterator uevent_listener_list_iterator;

	class remove_uevent_listener
	{
	public:

		remove_uevent_listener( uevent_listener* listener )
			: listener_( listener )
		{
		}

		bool operator()( uevent_listener* listener )
		{
			/* Always remove bad pointers */
			if ( !listener->is_valid() )
				return true;

			if ( listener_->is_valid() )
				return ( *listener_ == *listener );

			return false;
		}

	private:

		uevent_listener* listener_;
	};

	class find_uevent_listener
	{
	public:

		find_uevent_listener( uevent_listener* listener )
			: listener_( listener )
		{
		}

		bool operator()( uevent_listener* listener )
		{
			if ( listener_->is_valid() && listener->is_valid() )
				return ( *listener_ == *listener );

			return false;
		}

	private:

		uevent_listener* listener_;
	};

	class uengine_interface: public usingleton<uengine_interface>
	{
	public:

		uengine_interface( bool initialize = false )
			: initialized_( false )
		{
			if ( initialize )
				initialize_interface();
		}

		~uengine_interface()
		{
			cleanup_interface();
		}

		void initialize_interface();
		void cleanup_interface();

		bool is_initialized() const
		{
			return initialized_;
		}

		uevent_listener* add_event_listener( uevent_listener* listener );
		void remove_event_listener( uevent_listener* listener );

	private:

		bool initialized_;
		uevent_listener_list event_listeners_;
		uprocess_event_jmp* process_event_jmp_;
		uprocess_event_trampoline* process_event_trampoline_;

		static volatile process_event_type process_event_real;
		static void __stdcall process_event_code( uobject* caller, ufunction* function, void* parms, void* result );
	};	
}

#endif
